﻿using System.ComponentModel;
using Devonline.Communication.Messages;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace Devonline.Identity.Admin.Controllers;

[ApiController]
[Route("api/[controller]")]
[Description("访问规则管理")]
[Authorize(Roles = GROUP_MAINTAINERS)]
[AccessAuthorize(Code = "AS0XAccessRules", Name = "访问规则管理", ResourceType = ResourceType.Service, Roles = GROUP_MAINTAINERS)]
public class AccessRulesController(
    AuthorizationService authorizationService,
    ILogger<AccessRulesController> logger,
    IMessageCommunicator communicator,
    IDataService<AccessRule> dataService,
    IDataService<Resource> resourceService,
    IDataService<User> userService,
    IDataService<Role> roleService,
    IDataService<Group> groupService,
    IDataService<Level> levelService) :
    IdentityDataModelServiceController<AccessRule, AccessRuleViewModel>(logger, dataService)
{
    private readonly IMessageCommunicator _communicator = communicator;
    private readonly IDataService<Resource> _resourceService = resourceService;
    private readonly IDataService<User> _userService = userService;
    private readonly IDataService<Role> _roleService = roleService;
    private readonly IDataService<Group> _groupService = groupService;
    private readonly IDataService<Level> _levelService = levelService;
    private readonly AuthorizationService _authorizationService = authorizationService;

    [HttpGet, DisplayName("查询访问规则"), AccessAuthorize(Code = "AS0XAccessRulesIGet")]
    public override async Task<IActionResult> GetAsync() => Ok(await Request.GetPagedResultAsync(_dataService.GetQueryable().Include(x => x.Resource)));
    [HttpPost, DisplayName("创建访问规则"), AccessAuthorize(Code = "AS0XAccessRulesICreate")]
    public override async Task<IActionResult> CreateAsync(AccessRuleViewModel viewModel)
    {
        if (string.IsNullOrWhiteSpace(viewModel.ResourceIds))
        {
            return BadRequest("请选择资源列表!");
        }

        var resourceIds = viewModel.ResourceIds.Split(CHAR_COMMA, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
        if (resourceIds.Length == 0)
        {
            return BadRequest("请选择资源列表!");
        }

        if (!viewModel.IsAppend)
        {
            // 删除当前身份的现有授权
            await RemoveIdentityAccessRulesAsync(viewModel.IdentityType, viewModel.IdentityId);
        }

        // 对当前身份重新授权
        viewModel.ExpireTime = viewModel.ExpireTime?.ToUniversalTime();
        var accessRules = new List<AccessRule>();
        var identityCode = await GetIdentityCodeAsync(viewModel.IdentityType, viewModel.IdentityId);
        var resources = await _resourceService.GetQueryable(x => x.State == DataState.Available).ToListAsync();
        foreach (var resourceId in resourceIds)
        {
            var resource = resources.FirstOrDefault(x => x.Id == resourceId);
            if (resource is not null)
            {
                var accessRule = viewModel.CopyTo<AccessRule>();
                accessRule.Id = KeyGenerator.GetStringKey();
                accessRule.ResourceId = resource.Id;
                accessRule.Code = identityCode + resource.Code;
                accessRules.Add(accessRule);
            }
        }

        if (accessRules.Any())
        {
            await _dataService.AddsAsync(accessRules);
            await RefreshUserInfoAsync(viewModel.IdentityType, viewModel.IdentityId!);
        }

        return Ok(viewModel);
    }
    [HttpPut, DisplayName("更新访问规则"), AccessAuthorize(Code = "AS0XAccessRulesIUpdate")]
    public override async Task<IActionResult> UpdateAsync(AccessRuleViewModel viewModel)
    {
        if (viewModel.IdentityType == IdentityType.All)
        {
            viewModel.IdentityId = null;
        }

        if (viewModel.IdentityType == IdentityType.Anonymous || viewModel.IdentityType == IdentityType.System)
        {
            viewModel.IdentityId = _userService.GetQueryable(x => x.UserName == viewModel.IdentityType.ToString().ToLowerInvariant()).Select(x => x.Id).FirstOrDefault();
        }

        viewModel.ExpireTime = viewModel.ExpireTime?.ToUniversalTime();
        await _dataService.UpdateAsync(viewModel);
        await RefreshUserInfoAsync(viewModel.IdentityType, viewModel.IdentityId!);
        return Ok(viewModel);
    }
    [HttpDelete("{id}"), DisplayName("删除访问规则"), AccessAuthorize(Code = "AS0XAccessRulesIDelete")]
    public override async Task<IActionResult> DeleteAsync(string id)
    {
        var entitySet = await _dataService.GetIfExistAsync(id);
        await _dataService.DeleteAsync(id);
        await RefreshUserInfoAsync(entitySet.IdentityType, entitySet.IdentityId!);
        return Ok();
    }

    /// <summary>
    /// 此处的获取用户信息会自动刷新缓存
    /// </summary>
    /// <param identityName="userName"></param>
    /// <returns></returns>
    [HttpGet("GetUserInfo"), DisplayName("获取用户信息"), AccessAuthorize(Code = "AS0XAccessRulesIGetUserInfo")]
    public async Task<IActionResult> GetUserInfoAsync(string userName)
    {
        _logger.LogDebug($"user {HttpContext.GetUserName()} get user {userName} info forced");
        await _authorizationService.ClearCacheAsync(userName);
        var userInfo = await _authorizationService.GetUserInfoAsync(userName);
        await _communicator.NotifyUserInfoRefreshAsync(userName);
        return Ok(userInfo);
    }
    /// <summary>
    /// 根据身份类型和身份类别编号获取对应的授权资源
    /// </summary>
    /// <param identityName="identityType"></param>
    /// <param identityName="identityId"></param>
    /// <returns></returns>
    [HttpGet("GetIdentityResource/{identityType}/{identityId}"), DisplayName("按身份获取授权资源树"), AccessAuthorize(Code = "AS0XAccessRulesIGetIdentityResource")]
    public async Task<IActionResult> GetIdentityResourceAsync(IdentityType identityType, string identityId)
    {
        _dataService.SetTimeout(UNIT_MILLION);
        await _authorizationService.ClearCacheAsync(identityType, identityId);
        var resources = await _authorizationService.GetIdentityAccessResources(identityType, identityId);
        await RefreshUserInfoAsync(identityType, identityId);
        _dataService.SetTimeout();
        return Ok(resources);
    }

    /// <summary>
    /// 根据 Identity 的 Type 和 Id 构造 AccessRule 的 Code 前缀
    /// </summary>
    /// <param name="identityType"></param>
    /// <param name="identityId"></param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    private async Task<string> GetIdentityCodeAsync(IdentityType identityType, string? identityId = default)
    {
        var identityCode = string.Empty;
        switch (identityType)
        {
            case IdentityType.All:
                identityCode = "All@";
                break;
            case IdentityType.Anonymous:
            case IdentityType.System:
                var identity = await _roleService.FirstOrDefaultAsync(x => x.Type.ToString() == identityType.ToString());
                identityCode = $"{identity!.Name}@";
                break;
            case IdentityType.User:
                var user = await _userService.FirstOrDefaultAsync(x => x.Id == identityId);
                identityCode = $"{user!.UserName}#";
                break;
            case IdentityType.Role:
                var role = await _roleService.FirstOrDefaultAsync(x => x.Id == identityId);
                identityCode = $"{role!.Name}@";
                break;
            case IdentityType.Group:
                var group = await _groupService.FirstOrDefaultAsync(x => x.Id == identityId);
                identityCode = $"{group!.Name}+";
                break;
            case IdentityType.Level:
                var level = await _levelService.FirstOrDefaultAsync(x => x.Id == identityId);
                identityCode = $"{level!.Name}-";
                break;
            default:
                throw new BadHttpRequestException("暂不支持!");
        }

        return identityCode;
    }
    /// <summary>
    /// 移除指定身份的已分配访问规则列表
    /// </summary>
    /// <param name="identityType">身份类型</param>
    /// <param name="identityId">身份编号</param>
    /// <returns></returns>
    public async Task RemoveIdentityAccessRulesAsync(IdentityType identityType, string? identityId = default)
    {
        _logger.LogInformation($"user {_dataService.UserName} access will remove identity {identityType} id {identityId} access rules!");
        var queryable = _dataService.GetQueryable(x => x.IdentityType == identityType);
        if (!string.IsNullOrWhiteSpace(identityId))
        {
            queryable = queryable.Where(x => x.IdentityId == identityId);
        }

        var accessRules = await queryable.ToListAsync();
        if (accessRules.Count != 0)
        {
            await _dataService.DeletesAsync(accessRules);
        }

        _logger.LogInformation($"user {_dataService.UserName} access remove identity {identityType} id {identityId} access rules: {accessRules.Count}");
    }
    /// <summary>
    /// 刷新用户缓存
    /// </summary>
    /// <param identityName="identityType"></param>
    /// <param identityName="identityId"></param>
    /// <returns></returns>
    private async Task RefreshUserInfoAsync(IdentityType identityType, string identityId) => await _authorizationService.RefreshUserInfoAsync(identityType, identityId, _communicator.NotifyUserInfoRefreshAsync);
}